<?php

//setup file path
define('SetupPath', 'Classes/Utils/FileLift/fl_setup.inc');

class FileLiftCore {
	
	protected $conf;		//profile configuration
	protected $rootPath;	//server document root path
	protected $path;		//working path
	protected $tmpPath;		//working tmp path
	protected $cats;		//profile categories
	
	protected $data;		//array of data to work with
	protected $workCat;		//categorie to work with
	
	protected $errormsg;	//string with error explanation
	
	//CONSTRUCTOR
	/*Params:
	 * @prof: profile name
	 * @data: array with data to work
	 * 	=>id: session unique identifier
	 * @setup: setup file path (NOT REQUIRED)
	 * */
	
	function __construct($prof, $data, $storer = false, $setup = SetupPath) 
	{
		include($setup);
		if(!$FileLiftProfiles[$prof])
			$this->kill('WRONG PROFILE NAME. DEBUG INIT::: $prof='.$prof.':::DEBUG END.');
		//load profile from setup file, data from client
		$this->conf = $FileLiftProfiles[$prof]['conf'];
		$this->cats = $FileLiftProfiles[$prof]['categories'];
		$this->rootPath = $this->conf['absPath'];
		$this->data = $data;
		$this->sessionkey = 'FLtmpIndex_' . $prof;
		$this->status = '';
		
		if(!$storer)
			if($error = $this->checkMainFolders())
				$this->kill($error);
		
		//session tmp files index	(make sure you are using session_start()!!)
		if(!$this->getSessionIndex())
			$this->clearSessionIndex();
			
		return;
	}
	
	//
	//FILE WORKING METHODS
	//
	
	/*SEARCH FOR A FILE INTO THE FOLDER STRUCTURE
	 * @filename: name of the file
	 * @path: where to search, if not given the function will search into tmp path by default
	 * @only: search only into the path given (tmp path if not given)
	 * 				by default, if the file is not found into the path given to the function,
	 * 				the system will search for the file into the active workCat tmp path.
	 * returns:
	 * 		(bool) false if file not found
	 * 		(string) file path if exists
	 * */
	protected function find($filename, $path = null, $only = false)
	{
		if(!$path)	$path = $this->tmpPath;
		//check path
		if(!is_string($path) || !file_exists($path))	return false;
			
		// search only the path given
		if($only)
		{
			if(file_exists($path . '/' . $filename))
				return $path . '/' . $filename;
			else
				return false;
		}
		// search into the path given, tmpPath and into workPath
		$result = $path . '/' . $filename;
		if(!file_exists($result)) 
		{
			$result = $this->tmpPath . '/' . $filename;
			if(!file_exists($result))
			{
				$result = $this->path . '/' . $filename;
				if(!file_exists($result))
					return false;
			} 
		}
		return $result;
	}
	
	/*DELETE A FILE.
	 * @filepath: full path of the file to be deleted
	 * returns:
	 * 		true on sucess, false on failture
	 */ 
	protected function del($filepath) {
		if(file_exists($filepath))
			return unlink($filepath);
		return false;
	}
	
	/* STORE A FILE
	 * @pathfrom: full path of the file to store
	 * @pathto: destination path
	 * returns:
	 * 		the stored filename if the file was succesfully stored
	 * */
	protected function store($pathfrom, $pathto) {
		if(!file_exists($pathfrom))
			$this->kill('Wrong parameter given to store: pathfrom');

		//check if file exists & modify the name to not overwrite anything
		$i = 1;	
		while(file_exists($pathto)) 
		{
			//get the file, split extension and name
			$file = basename($pathto);
			$ext = substr($file, -4);
			
			//if file was previusly renamed with ($i), remove '($i)'
			$name = str_replace('(' . ($i-1) . ')', '', substr($file, 0, -4));
			
			//create filaname and assing new path
			$file = $name . '(' . $i . ')' . $ext;
			$pathto = dirname($pathto) . '/' . $file;
			++$i; 
		}
		//check if is a file uploaded via http POST, else rename it normally
		if(is_uploaded_file($pathfrom))	
		{			
			if(move_uploaded_file($pathfrom, $pathto))
				return basename($pathto);
		}
		else
		{
			if(rename($pathfrom, $pathto))
				return basename($pathto);
		}	
		return false;
	}
	
	//returns the name to be set on the file
	protected function ren($filename, $fileinfo)
	{
		if($this->data['id'] && $this->data['go'] != 'up')
		{
			$fileinfo['name'] = $filename;
			$result = $this->replaceTags($this->workCat['structure'], $fileinfo);
		}
		else
		{
			$s = $this->conf['tmp-spliter'];
			$result = date($this->conf['tmp-dateformat']) . $s . $this->data['cat'] . $s . $filename;
		}
		return $result;
	}
	
	/*returns array with file info
	 * @filepath: full path of the file to get info from
	 * */
	protected function getInfo($filepath) {
		if(!file_exists($filepath))
			return false;
			
		$fileinfo = array(
			'name' => basename($filepath),
			'size' => filesize($filepath),
			'filebasename' => basename($filepath)
		);
		
		//get original filename of tmp file
		$mixdown = str_replace($this->data['cat'], '', basename($filepath));
		$split = $this->conf['tmp-spliter'];
		while(strpos($mixdown, $split) !== false) {
			$mixdown = substr($mixdown, strpos($mixdown, $split) + strlen($split));
		}
		$fileinfo['basename'] = $mixdown;
		
		return $fileinfo;
	}
	
	//
	// DATA RESPONSE METHODS
	//
	
	//send Script with data to the client
	public function sendJS($script, $file) {
		$js = $this->replaceTags($script, $this->getInfo($file));
		die($js);
	}
	
	//send JSON data to the client
	public function sendJSON() {
		header("Content-Type: application/json");
		$json = array();
		$json['status'] = $this->status;
		$json['msg'] = $this->errormsg;
		$json['file'] = $this->data['file'];
		$json['obj'] = $this->data['obj'];
		die(json_encode($json));		
	}
	
	//stream file to the client
	protected function streamFile($filepath) {
	    header('Content-Description: File Transfer');
	    header('Content-Type: application/octet-stream');
	    header('Content-Disposition: attachment; filename='.basename($filepath));
	    header('Content-Transfer-Encoding: binary');
	    header('Expires: 0');
	    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
	    header('Pragma: public');
	    header('Content-Length: ' . filesize($filepath));
	    ob_clean();
	    flush();
	    readfile($filepath);
	    exit();
	}
	
	//kill the transaction and display ERROR
	protected function kill($error) {
		Throw new Exception($error);
		die('<p>FL ERROR: ' . $error . '</p>');
	}
	
	//
	// MESSAGE MANAGING METHODS
	//	
	
	/*replace tags with real values
	 * @string : string witch tags will be replaced
	 * @file : array with file properties
	 * 		=>'size'
	 * 		=>'name'
	 * 		=> 'basename' (original file name)
	*/
	protected function replaceTags($string, $file = array('name' => 'none', 'size' => 1024)) {
		if(!$file)
			$file = array('name' => 'none', 'size' => 1024);
			
		$tags = array('[id]', '[catname]', '[filename]', '[filesize]', '[filebasename]',
				 '[maxsize]', '[type]', 
				 '[status]', '[msg]', '[obj]'
				);
				
		$values = array($this->data['id'], $this->data['cat'], $file['name'], round($file['size']/1024, 2), $file['basename'],
				 $this->workCat['options']['maxsize'], $this->workCat['options']['type'],
				 $this->status, $this->errormsg, $this->data['obj']
				 );
				 
		return str_replace($tags, $values, $string);
	}
	
	
	//
	// CRASH PREVENTION METHODS
	//	
	
	
	//Checks profile's folder structure
		//returns false if its ok, string otherwise
	protected function checkMainFolders($id = null, $cat = null) {
		clearstatcache();
		//check if profile path exists and is writable
		if(!file_exists($this->rootPath) || !is_writable($this->rootPath))
			return 'WRONG PATH OR NO WRITABLE PATH. DEBUG_INIT:::profile[\'conf\'][\'absPath\'] = ' . $this->rootPath . ':::DEBUG_END';
		
		//privatefolders mode
		if(!$this->conf['privatefolders']) 
		{
			//@privatemode=false
			if($cat)	$this->setCat($cat);
				elseif($this->data['cat'])  $this->setCat($this->data['cat']);
			if(!$this->data['cat'])
				return 'CAN\'T START PRIVATEFOLDERS MODE IF NO CAT';
				
			//set paths
			$this->path = $this->rootPath . $this->workCat['path'];
			$this->tmpPath = $this->rootPath . $this->workCat['path'] . $this->conf['tmpPath'];
			
			//check folders
			if(!file_exists($this->path))
				mkdir($this->path);
			if(!file_exists($this->tmpPath))
				mkdir($this->tmpPath);
		}
		else 
		{
			//@privatemode=true
			if($id)		$this->setId($id);
			if($cat)	$this->setCat($cat);
				elseif($this->data['cat'])  $this->setCat($this->data['cat']);
			if(!$this->data['id'])
				return 'CAN\'T START PRIVATEFOLDERS MODE IF NO ID';
			if(!$this->data['cat'])
				return 'CAN\'T START PRIVATEFOLDERS MODE IF NO CAT';
				
			//set paths
			if(!file_exists($this->rootPath . $this->workCat['path']))
				mkdir($this->rootPath . $this->workCat['path']);
			$this->path = $this->rootPath . $this->workCat['path'] . '/' . $this->data['id'];
			$this->tmpPath = $this->rootPath . $this->workCat['path'] . $this->conf['tmpPath'];
			
			//check only for debugging, could trown and exception running normally 
			/* 
			 if( !( $n = is_writable($this->path) ) || !( $t = is_writable($this->path) ))
				return 'Some directories are not writable. DEBUG_INIT:::$this->path=' . (string) $n . ';$this->tmpPath=' . (string) $t . ':::DEBUG_END';
			*/  
				
			//check if id path exist, create it if dont
			if(!file_exists($this->path))
				mkdir($this->path);
			//check if tmp dir exists, create it if dont
			if(!file_exists($this->tmpPath))
				mkdir($this->tmpPath);
		}
		return false;
	}

	//
	// SESSION INDEX METHODS
	//
	
	protected function clearSessionIndex() {
		$_SESSION[$this->sessionkey] = array();
	}
	
	//returns session file index
	public function getSessionIndex() {
		return $_SESSION[$this->sessionkey];
	}
	
	//updates the session file index
	private function updateSessionIndex($arr) {
		$_SESSION[$this->sessionkey] = $arr;
	}
	
	//deletes entry into session file index
	protected function delIndexEntry($file) {
		$index = $this->getSessionIndex();
		$k = $this->getIndexKey($file);
		if(!$k)
			return false;
		array_splice($index, $k , 1);
		$this->updateSessionIndex($index);
		return true;
	}
	
	public function getIndexKey($file) {
		$index = $this->getSessionIndex();
		foreach($index as $key => $data) {
			if($data['filename'] == $file)
				return $key;
		}
		return false;
	}
	
	//sets new entry into session file index
	protected function setIndexEntry($filename, $status, $cat, $field) {
		$in = count($_SESSION[$this->sessionkey]);
		$_SESSION[$this->sessionkey][$in]['filename'] = $filename;
		$_SESSION[$this->sessionkey][$in]['status'] = $status;
		$_SESSION[$this->sessionkey][$in]['cat'] = $cat;
		$_SESSION[$this->sessionkey][$in]['field'] = $field;
	}
	
	//update a entry in the file index
	protected function updateIndexEntry($file, $newstatus, $reason = null) {
		$index = $this->getSessionIndex();
		$key = $this->getIndexKey($file);
		if(!$key)
			return;
		$index[$key]['status'] = $newstatus;
		if($reason)
			$index[$key]['reason'] = $reason;
		$this->updateSessionIndex($index);
	}
	
	//
	// INTERNAL DATA METHODS
	//
	
	//sets session identifier to let rename files and store them onto specific paths
	protected function setId($id) {
		if(!$id)
			return false;
		$this->data['id'] = $id;
		return true;
	}
	
	//sets working categorie
	//returns true on sucess, false on failture
	protected function setCat($cat) {
		if(!$this->cats[$cat])
			return false;
		$this->data['cat'] = $cat;
		$this->workCat = $this->cats[$cat];
		
		// update working paths
		$this->tmpPath = $this->rootPath . $this->workCat['path'] . $this->conf['tmpPath'];
		if($this->conf['privatefolders'])
			$this->path = $this->rootPath . $this->workCat['path'] . '/' . $this->data['id'];
		else
			$this->path = $this->rootPath . $this->workCat['path'];
		return true;
	}
}
?>